#coding:utf-8
import os,sys
import json
import numpy as np
from math import isnan
from django.shortcuts import render
import pymongo
import traceback
import copy
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '../'))
from CommonLib.db.MongoDBTool import MongoDBTool
from CommonLib.IndexCal.IndexCal import IndexCal
from CommonLib.SPGrammer.SenRecog import SenRecog

class FormatCal:
	# 获取一个list的第一个不为nan的位置，以及第二批为nan的位置的前一个位置
	# 返回[S,E],若S>E，表示无定位结果（一串非nan值）
	# 先判断结果再用，不然会遇到问题：[0：-1]表示无结果的，结果变成取到值了
	def get_dayoff_fromlist(self, nanlist):
		# from math import isnan
		listlen = len(nanlist)
		s = -1
		for i in range(0, listlen):
			if isnan(nanlist[i]):
				s += 1
			if not isnan(nanlist[i]):
				break
		s += 1
		e = s
		for i in range(s, listlen):
			if not isnan(nanlist[i]):
				e += 1
			if isnan(nanlist[i]):
				break
		e -= 1
		return [s, e]

	# dimension预处理
	#dimension_info[dimension_str] = {'multi_return':'N','index_form_list':[],'num_list':[],'op_list':[],'instead_str':''}
	def get_dimension_dict(self, dimensions):
		recog_obj = SenRecog()
		index_cal_obj = IndexCal()
		dimension_info = {}
		for dimension in dimensions.split('dimensionGap'):
			if dimension == '':
				continue
			dimension = dimension.lower()
			if dimension not in dimension_info:
				dimension_info[dimension] = {'multi_return':'N','index_form_list':[],'num_list':[],'op_list':[],'instead_str':''}
			else:
				continue
			try:
				index_str_list, instead_str_del_index = recog_obj.extract_index(recog_obj.clean_sentence(dimension))
				index_form_list = recog_obj.recog_index(index_str_list)
				num_list, instead_str_del_indexnum = recog_obj.extract_num(instead_str_del_index)
				op_list, instead_str_del_indexnumop = recog_obj.extract_op(instead_str_del_indexnum)
			except:
				del dimension_info[dimension]
				continue
			dimension_info[dimension]['num_list'] = num_list
			dimension_info[dimension]['op_list'] = op_list
			# --这里可改变formula计算方式（eval，或stake）
			dimension_info[dimension]['instead_str'] = instead_str_del_index
			cal_item_num = instead_str_del_indexnumop.count('N') + instead_str_del_indexnumop.count('I')
			continue_tag = False
			for index_form in index_form_list:
				child_str = index_form[0]
				index_name_str = index_form[1]
				params_float_list = index_form[2]
				day_offset_int = index_form[3]
				# 多child指标且参与计算，del and  break continue
				if (child_str =='' and (len(index_cal_obj.get_param_field(index_name_str)['child'])>1)) and (cal_item_num > 1):
					del dimension_info[dimension]
					continue_tag = True
					break
				# 多child指标不参与计算，add and break
				if (child_str =='' and (len(index_cal_obj.get_param_field(index_name_str)['child'])>1)) and (cal_item_num == 1):
					dimension_info[dimension]['multi_return'] = 'Y'
					child_str_list = index_cal_obj.get_param_field(index_name_str)['child']
					dimension_info[dimension]['index_form_list'].append([child_str_list,index_name_str,params_float_list,day_offset_int])
					break
				dimension_info[dimension]['index_form_list'].append([child_str,index_name_str,params_float_list,day_offset_int])
			if continue_tag is True:
				continue
		return dimension_info


	def cal_all_dims(self, dimension_info, period_s, period_e, code_str_list, adj, date_grain):
		recog_obj = SenRecog()
		index_cal_obj = IndexCal()
		mongodb_obj = MongoDBTool()
		# date_grain预处理（定义访问coll）
		if date_grain == 'day':
			mongodb_obj.conn('Supeanut', 'DayData')
		if date_grain == '5min':
			mongodb_obj.conn('Supeanut', 'FiveMinData')
		code_dict = {}
		for code in code_str_list:
			code_dict[code] = {}
		# info子key：basic_inputs(open,low,high,close,vol)
		st_input_key = ['open','low','high','close','vol_stock','vol_capital']
		if adj == 'ori':
			basic_input_key = ['open','low','high','close','vol_stock','vol_capital']
		elif adj == 'pre':
			basic_input_key = ['open_pre','low_pre','high_pre','close_pre','vol_stock_pre','vol_capital']
		elif adj == 'post':
			basic_input_key = ['open_post','low_post','high_post','close_post','vol_stock_post','vol_capital']
		else:
			pass
		# 填充code_dict的basic_inputs以及st_datetime
		hasMongoData_code_set = set([])
		for code in code_dict:
			code_dict[code]['st_datetime'] = []
			code_dict[code]['basic_input'] = {}
			for st_input in st_input_key:
				code_dict[code]['basic_input'][st_input] = []
			# mongo获取数据,按时间顺序
			if date_grain == 'day':
				docs = mongodb_obj.coll.find({'code':code, 'date':{'$gte':period_s.split(' ')[0], '$lte':period_e.split(' ')[0]}}).sort([('datetime',pymongo.ASCENDING)])
			# 待扩展的内容
			if date_grain == '5min':
				docs = mongodb_obj.coll.find({'code':code, 'date':{'$gte':period_s.split(' ')[0], '$lte':period_e.split(' ')[0]}}).sort([('datetime',pymongo.ASCENDING)])
			if docs.count() > 0:
				hasMongoData_code_set.add(code)
			for doc in docs:
				code_dict[code]['st_datetime'].append(doc['datetime'])
				for basic_input_i in range(len(basic_input_key)):
					code_dict[code]['basic_input'][st_input_key[basic_input_i]].append(float(doc[basic_input_key[basic_input_i]]))
			# 补充datetime到input
			code_dict[code]['basic_input']['datetime'] = code_dict[code]['st_datetime']
		# 填充code_list的dimension_result
		for code in hasMongoData_code_set:
			code_dict[code]['dimension_result'] = {}
			# 处理每个dimension，将结果list添加
			for dimension in dimension_info:
				# 多维返回，只进行指标计算
				if dimension_info[dimension]['multi_return'] == 'Y':
					index_form = dimension_info[dimension]['index_form_list'][0]
					child_str_list = index_form[0]
					index_name = index_form[1]
					params = index_form[2]
					dayoffset = index_form[3]
					input_dict = code_dict[code]['basic_input']
					result_list = index_cal_obj.cal_index(index_name, input_dict, params, dayoffset, return_child=None)
					for i in range(len(result_list)):
						demension_result_name = child_str_list[i] + '@' + dimension
						demension_result_list = result_list[i]
						# date_series 不用移动
						if index_name in index_cal_obj.date_series:
							date_len = len(code_dict[code]['st_datetime'])
							if date_len == 0:
								date_off = [-1, -1]
							else:
								date_off = [0, date_len-1]
						else:
							date_off = self.get_dayoff_fromlist(demension_result_list)
						if demension_result_name not in code_dict[code]['dimension_result']:
							code_dict[code]['dimension_result'][demension_result_name] = {}
							code_dict[code]['dimension_result'][demension_result_name]['date_off'] = date_off
							code_dict[code]['dimension_result'][demension_result_name]['result_list'] = demension_result_list
					continue
				# 单值（或带公式）计算
				# --中间结果计算
				midware_results = []
				midware_results_dayoff = []
				index_form_list = dimension_info[dimension]['index_form_list']
				date_series_tag = False
				for index_form in index_form_list:
					child_str = index_form[0]
					index_name = index_form[1]
					params = index_form[2]
					dayoffset = index_form[3]
					input_dict = code_dict[code]['basic_input']
					result_list = index_cal_obj.cal_index(index_name, input_dict, params, dayoffset, child_str)
					# date_series 直接返回
					if index_name in index_cal_obj.date_series:
						date_len = len(code_dict[code]['st_datetime'])
						if date_len == 0:
							date_off = [-1, -1]
						else:
							date_off = [0, date_len-1]
						code_dict[code]['dimension_result'][index_name+'(0)']= {}
						code_dict[code]['dimension_result'][index_name+'(0)']['date_off'] = date_off
						code_dict[code]['dimension_result'][index_name+'(0)']['result_list'] = result_list
						date_series_tag = True
						break
					else:
						date_off = self.get_dayoff_fromlist(result_list)
					midware_results.append(result_list)
					midware_results_dayoff.append(date_off)
				if date_series_tag is True:
					continue
				# --中间结果全nan或无重复时间区间的检查
				midware_noresult_tag = False
				max_left = -1
				min_right = len(code_dict[code]['st_datetime'])+1
				for dayoff in midware_results_dayoff:
					if dayoff[0] > max_left:
						max_left = dayoff[0]
					if dayoff[1] < min_right:
						min_right = dayoff[1]
					if dayoff[0] > dayoff[1]:
						# 存在全nan list
						midware_noresult_tag= True
						break
				if max_left > min_right:
					midware_noresult_tag = True
				if midware_noresult_tag == True:
					continue
				# --合并中间结果
				combine_midware_result = []
				# ---合并每天的各index的值，作为公式输入
				midware_index_value_list = []
				for i in range(max_left, min_right+1):
					oneday_midware_index_value_list = []
					for midware_result in midware_results:
						oneday_midware_index_value_list.append(midware_result[i])
					midware_index_value_list.append(oneday_midware_index_value_list)
				# ---计算每天的formula值
				for oneday_midware_index_value_list in midware_index_value_list:
					instead_str = dimension_info[dimension]['instead_str']
					# --这里可改变formula计算方式，通过换函数
					combine_midware_result.append(recog_obj.calculate_value_by_eval(instead_str, oneday_midware_index_value_list))
				# ---左边补max_left个nan，右边补 len(code_dict[code]['basic_input']['datetime'])-min_right-1 个nan
				right_neednum = len(code_dict[code]['st_datetime'])-min_right-1
				left_neednum = max_left
				combine_midware_result.extend([np.nan for i in range(right_neednum)])
				combine_midware_result = [np.nan for i in range(left_neednum)] + combine_midware_result
				# 将结果添加到code_dict
				code_dict[code]['dimension_result'][dimension]= {}
				code_dict[code]['dimension_result'][dimension]['date_off'] = [max_left, min_right]
				code_dict[code]['dimension_result'][dimension]['result_list'] = combine_midware_result
		# 计算code下的dateoff,以及可用demension
		allcode_aviable_dimension_set = set([])
		for code in hasMongoData_code_set:
			code_level_dayoff_left = -1
			code_level_dayoff_right = len(code_dict[code]['st_datetime'])+1
			aviable_dimension = []
			for dimension_name in code_dict[code]['dimension_result']:
				dimension_result_dayoff = code_dict[code]['dimension_result'][dimension_name]['date_off']
				if code_level_dayoff_left < dimension_result_dayoff[0]:
					code_level_dayoff_left = dimension_result_dayoff[0]
				if code_level_dayoff_right > dimension_result_dayoff[1]:
					code_level_dayoff_right = dimension_result_dayoff[1]
				if dimension_result_dayoff[0] <= dimension_result_dayoff[1] and dimension_result_dayoff[0] >=0:
					aviable_dimension.append(dimension_name)
					allcode_aviable_dimension_set.add(dimension_name)
			code_dict[code]['codelevel_date_off'] = [code_level_dayoff_left, code_level_dayoff_right]
			code_dict[code]['aviable_dimension'] = aviable_dimension
		# all_code level下的共有dimension
		for code in hasMongoData_code_set:
			aviable_dimension = code_dict[code]['aviable_dimension']
			allcode_aviable_dimension_set = allcode_aviable_dimension_set.intersection(set(aviable_dimension))
		allcode_aviable_dimension_list = list(allcode_aviable_dimension_set)
		code_dict['allcode_aviable_dimension_list'] = allcode_aviable_dimension_list
		code_dict['hasMongoData_code_set'] = hasMongoData_code_set
		return code_dict

	def get_inds(self, code_dict):
		index_cal_obj = IndexCal()
		render_dict = {}
		render_dict['indivsiual'] = []
		render_dict['field'] = ['datetime','code'] + code_dict['allcode_aviable_dimension_list']
		for code in code_dict['hasMongoData_code_set']:
			no_common_tag = False
			codelevel_date_off = code_dict[code]['codelevel_date_off']
			if codelevel_date_off[0] > codelevel_date_off[1] or (codelevel_date_off[0]<0 and codelevel_date_off[1]<0):
				continue
			code_dimension_result = code_dict[code]['dimension_result']
			st_datetime = code_dict[code]['st_datetime']
			for i in range(codelevel_date_off[0], codelevel_date_off[1]+1):
				indivsiual = [st_datetime[i], code]
				for dimension_name in code_dict['allcode_aviable_dimension_list']:
					indivsiual.append(code_dimension_result[dimension_name]['result_list'][i])
				render_dict['indivsiual'].append(indivsiual)
		# field_name:field_dim 对应字典 如'code':'1','kdj':2
		field_name_dim_dict = {}
		for field_name in render_dict['field']:
			field_name_dim_dict[field_name] = render_dict['field'].index(field_name)
		render_dict['field_name_dim'] = field_name_dim_dict
		# field_name:field_class 对应字典 如'kdj':'value','datetime':'datetime','ma>ma''code''day_week':'category'
		date_series_names = index_cal_obj.get_date_series_name()
		field_name_class_dict = {'datetime':'datetime','code':'category_str'}
		for field_name in code_dict['allcode_aviable_dimension_list']:
			# day_series判定
			# --取day_week(0)的day_week部分
			if field_name[:field_name.find('(')] in date_series_names:
				field_name_class_dict[field_name] = 'category_str'
			elif isinstance(render_dict['indivsiual'][0][field_name_dim_dict[field_name]],bool):
				field_name_class_dict[field_name] = 'category_bool'
			else:
				field_name_class_dict[field_name] = 'value'
		render_dict['field_name_class'] = field_name_class_dict
		# 对个体按日期进行排序
		render_dict['indivsiual'].sort(key = lambda k:k[0])
		# code_list添加到结果
		render_dict['code_list'] = list(code_dict['hasMongoData_code_set'])
		return render_dict
